using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Windows.Forms;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using Athena.Core;

namespace Athena.Objects
{
    [Serializable()]
    public class PartGlyph : IGlyph
    {
        protected Color _oPartColor = Color.LightSteelBlue;


        [CategoryAttribute("Part Attributes"), Description("Color of the part image.")]
        public Color PartColor
        {
            get { return _oPartColor; }
            set { _oPartColor = value; }
        }

        /// <summary>
        /// the part that is connected to the left of this part
        /// </summary>
        [CategoryAttribute("Part Attributes"), Description("the part connected to the left of this part")]
        public virtual PartGlyph Left
        {
            get
            {
                if (_Item == null || !(_Item is Part)) return null;
                if (((Part)_Item).Left == null)
                    return null;
                return ((Part)_Item).Left.Drawable as PartGlyph;
            }
            set
            {
                if (value != null) View = value.View;

                if (_Item == null || !(_Item is Part)) return;

                Part part = (Part)_Item;

                if (value == null)
                {
                    part.Left = null;
                }
                else
                {
                    PartGlyph oldLeft = Left;
                    PartGlyph oldRight = value.Right;
                    part.Left = value.Item as Part;
                    if (oldLeft != null && Parent != null && oldLeft.Parent != Parent && Left.Parent == Parent)
                    {
                        LeftMost.Left = oldLeft;
                    }
                    if (oldRight != null && Parent != null && oldRight.Parent != Parent && Right.Parent == Parent)
                    {
                        value.RightMost.Right = oldRight;
                    }
                }
                foreach (Connector c in _IncomingConnections)
                {
                    foreach (IDrawable id in c.Reactants)
                    {
                        if (id.Item is Node)
                            part.PoPS.AddModifier((Node)id.Item);
                    }
                }
                UpdateLocationLeft();
            }
        }

        /// <summary>
        /// The part that is connected to the right of this part
        /// </summary>
        [CategoryAttribute("Part Attributes"), Description("the part connected to the right of this part")]
        public virtual PartGlyph Right
        {
            get
            {
                if (_Item == null || !(_Item is Part)) return null;
                if (((Part)_Item).Right == null)
                    return null;
                return ((Part)_Item).Right.Drawable as PartGlyph;
            }
            set
            {
                if (value != null) View = value.View;

                if (_Item == null || !(_Item is Part)) return;

                Part part = (Part)_Item;

                if (value == null)
                {
                    part.Right = null;
                }
                else
                {
                    PartGlyph oldRight = Right;
                    PartGlyph oldLeft = value.Left;
                    part.Right = value.Item as Part;
                    if (oldRight != null && Parent != null && oldRight.Parent != Parent && Right.Parent == Parent)
                    {
                        RightMost.Right = oldRight;
                    }
                    if (oldLeft != null && Parent != null && oldLeft.Parent != Parent && Right.Parent == Parent)
                    {
                        value.LeftMost.Left = oldLeft;
                    }
                }
                foreach (Connector c in _IncomingConnections)
                {
                    foreach (IDrawable id in c.Reactants)
                    {
                        if (id.Item is Node)
                            part.PoPS.AddModifier((Node)id.Item);
                    }
                }
                UpdateLocationRight();
            }
            
        }

        /// <summary>
        /// The type of view for this part -- block or schematic
        /// </summary>
        public override IDrawable.ViewType View
        {
            get
            {
                return _ViewType;
            }
            set
            {
                base.View = value;
                if (Right != null && Right.View != _ViewType) Right.View = _ViewType;
                if (Left != null && Left.View != _ViewType) Left.View = _ViewType;
            }
        }

        /// <summary>
        /// the current through this part going to the right
        /// </summary>
        [CategoryAttribute("Part Attributes"), Description("Polymerase current from left to right")]
        public virtual string PoPS_Right
        {
            get
            {
                if (_Item is Part)
                {
                    return (_Item as Part).PoPS_Right.Rate;
                }
                else
                    return "NA";
            }
            set
            {
                if (_Item is Part)
                {
                    ((Part)_Item).PoPS_Right.Rate = value;

                    foreach (Connector c in _IncomingConnections)
                    {
                        foreach (IDrawable id in c.Reactants)
                        {
                            if (id.Item is Node)
                                ((Part)_Item).PoPS.AddModifier((Node)id.Item);
                        }
                    }
                }
            }
        }

        [CategoryAttribute("Part Attributes"), Description("The direction in which this DNA part will be read")]
        public virtual Part.Orientation Direction
        {
            get
            {
                if (_Item is Part)
                {
                    return ((Part)_Item).Direction;
                }
                else
                    return 0;
            }
            set
            {
                if (_Item is Part)
                {
                    ((Part)_Item).Direction = (value);
                }
            }
        }

        [CategoryAttribute("Part Attributes"), Description("Polymerase current from right to left")]
        public virtual string PoPS_Left
        {
            get
            {
                if (_Item is Part)
                {
                    return (_Item as Part).PoPS_Left.Rate;
                }
                else
                    return "";
            }
            set
            {
                if (_Item is Part)
                {
                    ((Part)_Item).PoPS_Left.Rate = value;

                    foreach (Connector c in _IncomingConnections)
                    {
                        foreach (IDrawable id in c.Reactants)
                        {
                            if (id.Item is Node)
                                ((Part)_Item).PoPS.AddModifier((Node)id.Item);
                        }
                    }
                }
            }
        }

        [CategoryAttribute("Part Attributes"), Description("Sequence of this Part")]
        public virtual string Sequence
        {
            get
            {
                if (_Item != null && _Item is Part)
                    return ((Part)_Item).Sequence;
                else
                    return "";
            }
            set
            {
                if (_Item != null && _Item is Part)
                    ((Part)_Item).Sequence = (value);
            }
        }

        public override bool HasPreview
        {
            get
            {
                return true;
            }
        }

        public override Bitmap GetPreview(int width, int height)
        {
            Bitmap oBitmap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(oBitmap);

            RenderIcon(g, width, height);

            return oBitmap;
        }

        public virtual void RenderIcon(Graphics g, float width, float height)
        {
            Pen oOutline = new Pen(_oEdgeColor, 0.7f),
                oLines = new Pen(_oFillColorStart, _dEdgeThickness / 2);

            Brush oFilled;
            if (_oFillColorStart == _oFillColorEnd || _Dimensions.Width < 2 || _Dimensions.Height < 2)
                oFilled = new SolidBrush(_oFillColorStart);
            else
                oFilled = new LinearGradientBrush(Location, new PointF(Location.X + Dimensions.Width, Location.Y + Dimensions.Height), _oFillColorStart, _oFillColorEnd);

            g.DrawRectangle(oOutline, 1.5f, 1.5f, width-3f, height-3f);


            //g.FillRectangle(new SolidBrush(Color.Snow), 0, 0, width, height);

            float x = 1, y = 1,
                  h = height/2 - 2,
                  x_max = width - 4,
                  w = width/6;
            while (x < x_max)
            {
                g.DrawLine(oLines, Math.Min(x_max, x + w), y + 2 * h - 5, Math.Min(x_max, x + w), y + 5);
                g.DrawLine(oLines, Math.Min(x_max, x + w * 0.5F), y + 2 * h - 8, Math.Min(x_max, x + w * 0.5F), y + 8);
                g.DrawLine(oLines, Math.Min(x_max, x + w * 1.5F), y + 2 * h - 8, Math.Min(x_max, x + w * 1.5F), y + 8);
                g.DrawBezier(oOutline, x, y + h, Math.Min(x_max, x + w), y, Math.Min(x_max, x + w), y, Math.Min(x_max, x + 2 * w), y + h);
                g.DrawBezier(oOutline, x, y + h, Math.Min(x_max, x + w), y + 2 * h, Math.Min(x_max, x + w), y + 2 * h, Math.Min(x_max, x + 2 * w), y + h);
                x += 2 * w;
            }
        }

        /// <summary>
        /// Initializes a new instance of the PartGlyph class.
        /// </summary>
        /// <param name="location"></param>
        public PartGlyph(PointF location)
        {
            TextH = HorizontalAlignment.Center;
            TextV = VerticalAlignment.BelowBottom;
            _Location = location;
            _OldLocation = _Location;
            _Dimensions = new SizeF(60, 40);
            _oFillColorStart = _oFillColorEnd = Color.Beige;
            _oEdgeColor = Color.MidnightBlue;
            Item = new Part("Part", "Part_0", "");
            _oLabelBounds.Y += _Dimensions.Height;

            LoadDefaults(DEFAULT);
        }

        public PartGlyph()
        {
            TextH = HorizontalAlignment.Center;
            TextV = VerticalAlignment.BelowBottom;
            _Location = new PointF(0, 0);
            _Dimensions = new SizeF(60,46);
            _oLabelBounds.Y = 50;
            _bLabelBoundsChanged = true;
            _oFillColorStart = _oFillColorEnd = Color.Beige;
            _oEdgeColor = Color.MidnightBlue;
            _Item = new Part();

            LoadDefaults(DEFAULT);
        }

        public override object Clone()
        {
            return new PartGlyph(this);
        }

        public PartGlyph(PartGlyph copy)
        {
            Location = copy.Location;
            Dimensions = copy.Dimensions;
            Text = copy.Text;
            LabelBounds = copy.LabelBounds;
            TextColor = copy.TextColor;
            TextFont = copy.TextFont;            
            Type = copy.Type;
            ColorStart = copy.ColorStart;
            ColorEnd = copy.ColorEnd;
            PartColor = copy.PartColor;
            EdgeColor = copy.EdgeColor;
            EdgeThickness = copy.EdgeThickness;
            ColorStart = copy.ColorStart;
            Immobile = copy.Immobile;
            _IncomingConnections = new List<Connector>();
            _OutgoingConnections = new List<Connector>();                 
            Name = copy.Name;
            if (copy.Item != null)
                Item = (Item)copy.Item.Clone();
            Parent = null;       
            Sequence = copy.Sequence;
        }

        /// <summary>
        /// Draws the PartGlyph box with the left/right connections
        /// </summary>
        /// <param name="g"></param>
        protected virtual void DrawBox(Graphics g)
        {
            Pen pen = new Pen(Color.Black, 2);

            if (IsSelected)
                pen = new Pen(Color.Red, 2);

            if (_AttachmentPoint != _Location)
            {
                Utility.DrawLines(g,pen, Center, _AttachmentPoint);
                List<IDrawable> hits = Program.Model.FindItems(_AttachmentPoint);
                foreach (IDrawable id in hits)
                {
                    if (id is PartGlyph)
                    {
                        g.DrawRectangle(pen, id.Location.X, id.Location.Y, id.Dimensions.Width, id.Dimensions.Height);
                        break;
                    }
                }
            }

            Pen oOutline = new Pen(_oEdgeColor, _dEdgeThickness);
            RectangleF oTemp = Bounds, oTempSmall = Bounds;

            Brush oFilled;
            if (_oFillColorStart == _oFillColorEnd || _Dimensions.Width < 2 || _Dimensions.Height < 2)
                oFilled = new SolidBrush(_oFillColorStart);
            else
                oFilled = new LinearGradientBrush(Location, new PointF(Location.X + Dimensions.Width, Location.Y + Dimensions.Height), _oFillColorStart, _oFillColorEnd);

            oTempSmall.Inflate(-1.5f * _dEdgeThickness, -1.5f * _dEdgeThickness);
            g.FillRectangle(oFilled, oTempSmall);

            oTemp.Inflate(-_dEdgeThickness, -_dEdgeThickness);

            g.DrawRectangle(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
            //just need to draw one side to avoid redundant lines

            float ballsz = _Dimensions.Height / 10f;

            if (Left != null && Left.IsVisible && !(Left.IsSelected && !IsSelected))
            {
                Utility.DrawLines(g, pen, new PointF(Left.Bounds.Right, Left.Center.Y),
                                          new PointF(_Location.X, _Location.Y + _Dimensions.Height / 2));
            }
            else
                if (IsSelected)
                    g.FillEllipse(pen.Brush, _Location.X - ballsz - 5, _Location.Y + _Dimensions.Height / 2 - ballsz, 2 * ballsz, 2 * ballsz);

            if (Right != null && Right.IsVisible && !(Right.IsSelected && !IsSelected))
            {
                if (IsSelected)
                    Utility.DrawLines(g, pen, new PointF(_Location.X + _Dimensions.Width, _Location.Y + _Dimensions.Height / 2),
                                              new PointF(Right.Bounds.Left, Right.Center.Y));
                
            }
            else
                if (IsSelected)
                    g.FillEllipse(pen.Brush, _Location.X + _Dimensions.Width - ballsz + 5, _Location.Y + _Dimensions.Height / 2 - ballsz, 2 * ballsz, 2 * ballsz);
        }

        /// <summary>
        /// renders the same as PartGlyph by adds red lines if Left or Right are other DNAGlyph
        /// </summary>
        /// <param name="g"></param>
        public override void RenderGlyph(Graphics g)
        {
            Pen oOutline = new Pen(_oEdgeColor, _dEdgeThickness),
                oLines = new Pen(_oEdgeColor, _dEdgeThickness / 2);

            RectangleF oTemp = Bounds;
            
            if (View == ViewType.Schematic)
            {
                g.DrawLine(oOutline, oTemp.Left-5, oTemp.Bottom, oTemp.Right+5, oTemp.Bottom);
                return;
            }
            
            DrawBox(g);           

            float x = oTemp.X + 10,
                  h = Math.Min(40,oTemp.Height/2),
                  y = oTemp.Y + (oTemp.Height - h)/2,
                  x_max = oTemp.X + oTemp.Width - 10,
                  w = Math.Min(20,oTemp.Width/4);
            while (x+2*w < x_max)
            {
                g.DrawLine(oLines, Math.Min(x_max, x + w), y + 0.2f*h, Math.Min(x_max, x + w), y + 0.8f*h);
                g.DrawLine(oLines, Math.Min(x_max, x + w * 0.5F), y + 0.75f * h, Math.Min(x_max, x + w * 0.5F), y + 0.25f*h);
                g.DrawLine(oLines, Math.Min(x_max, x + w * 1.5F), y + 0.75f * h, Math.Min(x_max, x + w * 1.5F), y + 0.25f*h);
                g.DrawBezier(oOutline, x, y + h/2, Math.Min(x_max, x + w), y, Math.Min(x_max, x + w), y, Math.Min(x_max, x + 2 * w), y + h/2);
                g.DrawBezier(oOutline, x, y + h/2, Math.Min(x_max, x + w), y + h, Math.Min(x_max, x + w), y + h, Math.Min(x_max, x + 2 * w), y + h/2);
                x += 2 * w;
            }

        }

        /// <summary>
        /// In addition to moving the drawable, this method also adjusts the position of connected parts
        /// </summary>
        /// <param name="oNewPoint"></param>
        public override void MoveTo(PointF oNewPoint)
        {
            if (!Immobile)
            {
                base.MoveTo(oNewPoint);
                /*if (Control.ModifierKeys == Keys.Shift)
                {
                    Left = null;
                    Right = null;
                }
                else
                {*/
                if (IsSelected) UpdateLocation();
                //}
            }
        }

        public override void HitBy(IDrawable drawable, bool moving)
        {
            if (moving) return;
            if (!(Bounds.IntersectsWith(drawable.Bounds))) return;

            if (Parent != null) Parent.HitBy(drawable, false);

            base.HitBy(drawable, moving);        
            if (!(drawable is PartGlyph)) return;

            PartGlyph target = (PartGlyph)drawable;

            if (target == Left || target == Right) return;

            if (drawable.Location.X < _Location.X)
            {
                LeftMost.Left = target;
            }
            else
            {
               RightMost.Right = target;
            }
            UpdateLocation();
        }
        /// <summary>
        /// Get all the parts that are connected to this part (left to right order)
        /// Only parts with the same Parent are included
        /// </summary>
        [CategoryAttribute("Part Attributes"), Description("all parts connected to this one in this module")]        
        public virtual List<PartGlyph> ConnectedParts
        {
            get
            {
                List<PartGlyph> list = new List<PartGlyph>();

                PartGlyph leftmost = LeftMost, rightmost = RightMost;

                while (leftmost != null && leftmost != rightmost)
                {
                    list.Add(leftmost);
                    leftmost = leftmost.Right;
                }
                list.Add(rightmost);

                return list;
            }
        }

        protected PointF _AttachmentPoint;
        public override PointF Location
        {
            get
            {
                return base.Location;
            }
            set
            {
                base.Location = value;
                _AttachmentPoint = _Location;
            }
        }


        public override void DoneModifying(Corner oCorner, bool modifyAnchoredItems)
        {
            List<PartGlyph> parts = ConnectedParts;
            if (_AttachmentPoint != _Location)
            {
                RectangleF rect = new RectangleF(_AttachmentPoint.X - 10, _AttachmentPoint.Y - 10, 20, 20);
                List<IDrawable> hits = Program.Model.FindItems(rect);
                bool none = true;
                foreach (IDrawable id in hits)
                {
                    if (id is PartGlyph && id != this)
                    {
                        if (oCorner == Corner.ControlPoint)
                        {
                            if (Left == id)
                                Left = null;
                            else
                                Left = (PartGlyph)id;
                        }
                        else
                        {
                            if (Right == id)
                                Right = null;
                            else
                                Right = (PartGlyph)id;
                        }
                        none = false;
                        break;
                    }
                }
                _AttachmentPoint = _Location;

                if (none)
                {
                    if (oCorner == Corner.ControlPoint)
                        Left = null;
                    else
                        Right = null;
                }
            }

            foreach (IDrawable id in parts)
                if (id.Parent != null)
                    id.Parent.ChildChanged(id);
            UpdateLocation();
            base.DoneModifying(oCorner, modifyAnchoredItems);
        }
        /// <summary>
        /// In addition to transforming the drawable, this method also adjusts the position of connected parts
        /// </summary>
        /// <param name="oNewPoint"></param>
        public override void Transform(PointF oDirection, ref Corner oCorner, bool Ctrl)
        {
            base.Transform(oDirection, ref oCorner, Ctrl);
            UpdateLocation();
            if (oCorner == Corner.ControlPoint)
            {
                if (_AttachmentPoint == _Location)
                    _AttachmentPoint = new PointF(_Location.X, _Location.Y + _Dimensions.Height / 2);
                else
                    _AttachmentPoint = new PointF(_AttachmentPoint.X + oDirection.X,
                                                  _AttachmentPoint.Y + oDirection.Y);
            }
            else
                if (oCorner == Corner.ControlPoint + 1)
                {
                    if (_AttachmentPoint == _Location)
                        _AttachmentPoint = new PointF(oDirection.X + _Location.X + _Dimensions.Width,
                                                      oDirection.Y + _Location.Y + _Dimensions.Height / 2);
                    else
                        _AttachmentPoint = new PointF(_AttachmentPoint.X + oDirection.X,
                                                      _AttachmentPoint.Y + oDirection.Y);
                }
                else
                {
                    _AttachmentPoint = _Location;
                }
            //INIT_SZ = _Dimensions;
        }

        /// <summary>
        /// changes the position of the partglyphs to the left of this glyph
        /// </summary>
        private void UpdateLocationLeft()
        {
            if (Left == null) return;

            float midy = _Location.Y + _Dimensions.Height / 2,  //center line for all connected parts
                  startx = _Location.X - Left.Dimensions.Width - 5; //x position where next part should start

            float height = Dimensions.Height;
            PartGlyph rightmost = Left;
            IDrawable parent = Parent;
            PointF newlocation;

            while (rightmost != null && rightmost != this && rightmost.Parent == parent)
            {
                newlocation = new PointF(startx, midy - rightmost.Dimensions.Height / 2);

                float threshold = 50f; //rightmost.Dimensions.Width;

                if (Utility.Distance(rightmost.Location, newlocation) > threshold) break;

                if (rightmost.IsVisible && rightmost.Location != newlocation && !rightmost.IsSelected)
                {
                    rightmost.MoveTo(newlocation);
                    rightmost.Dimensions = new SizeF(rightmost.Dimensions.Width,height);
                    if (parent is ContainerGlyph && parent.Bounds.Contains(Bounds)) ((ContainerGlyph)parent).AdjustSize(rightmost);
                }

                if (rightmost.Left != null)
                    startx = rightmost.Location.X - rightmost.Left.Dimensions.Width - 5;

                rightmost = rightmost.Left;
            }
        }

        /// <summary>
        /// changes the position of the partglyphs to the right of this glyph
        /// </summary>
        private void UpdateLocationRight()
        {
            if (Right == null) return;

            float midy = _Location.Y + _Dimensions.Height / 2,  //center line for all connected parts
                  startx = _Location.X + _Dimensions.Width + 5; //x position where next part should start

            float height = Dimensions.Height;
            PartGlyph leftmost = Right;
            IDrawable parent = Parent;
            PointF newlocation;

            while (leftmost != null && leftmost != this && leftmost.Parent == parent)
            {
                newlocation = new PointF(startx, midy - leftmost.Dimensions.Height / 2);

                float threshold = 50f; //leftmost.Dimensions.Width;

                if (Utility.Distance(leftmost.Location, newlocation) > threshold) break;

                if (leftmost.IsVisible && leftmost.Location != newlocation && !leftmost.IsSelected)
                {
                    leftmost.MoveTo(newlocation);
                    leftmost.Dimensions = new SizeF(leftmost.Dimensions.Width, height);
                    if (parent is ContainerGlyph && parent.Bounds.Contains(Bounds)) 
                        ((ContainerGlyph)parent).AdjustSize(leftmost);
                }

                if (leftmost.Right != null)
                    startx = leftmost.Location.X + leftmost.Dimensions.Width + 5;

                leftmost = leftmost.Right;
            }
        }

        /// <summary>
        /// This method adjusts all the attached PartGlyphs so that they are centered at the same y-axis
        /// </summary>
        public virtual void UpdateLocation()
        {
            UpdateLocationLeft();
            UpdateLocationRight();
        }

        /// <summary>
        /// Get the left-most part in this chain of parts within this module
        /// </summary>
        [CategoryAttribute("Part Attributes"), Description("left most part")]        
        public virtual PartGlyph LeftMost
        {
            get
            {
                PartGlyph left = this;
                while (left.Left != null && left.Left != this && left.Left.Parent == Parent)
                    left = left.Left;
                return left;
            }
        }

        /// <summary>
        /// Get the right-most part in this chain of parts within this module
        /// </summary>
        [CategoryAttribute("Part Attributes"), Description("left most part")]        
        public virtual PartGlyph RightMost
        {
            get
            {
                PartGlyph right = this;
                while (right.Right != null && right.Right != this && right.Right.Parent == Parent)
                    right = right.Right;
                return right;
            }
        }

        /// <summary>
        /// Remove this instance from the model
        /// </summary>
        public override void Remove()
        {
            Right = null;
            Left = null;
            base.Remove();
        }

        /// <summary>
        /// Find the corner or the link at this point
        /// </summary>
        /// <param name="oPoint"></param>
        /// <returns></returns>
        public override Corner Where(PointF oPoint)
        {
            Corner corner = base.Where(oPoint);

            if (corner == Corner.None)
            {
                RectangleF oBounds = Bounds;

                float sz = 10f;
                RectangleF right = new RectangleF(oBounds.Right - sz, (oBounds.Top + 0.5f * oBounds.Height) - sz, 2*sz, 2*sz);
                RectangleF left = new RectangleF(oBounds.Left - sz, (oBounds.Top + 0.5f * oBounds.Height) - sz, 2*sz, 2*sz);

                if (left.Contains(oPoint))
                {
                    //if (!LinkClicked(Corner.ControlPoint))
                        return Corner.ControlPoint;
                }
                if (right.Contains(oPoint))
                {
                    //if (!LinkClicked(Corner.ControlPoint + 1))
                        return Corner.ControlPoint + 1;
                }

                return Corner.None;
            }
            else
                return corner;
        }

        /// <summary>
        /// The is called when a link (red bar) is clicked -- called by Where(PointF)
        /// </summary>
        /// <param name="oCorner"></param>
        protected virtual bool LinkClicked(Corner oCorner)
        {
            float sz = 20;
            RectangleF oBounds = Bounds;

            if (oCorner == Corner.ControlPoint)
            {
                if (Left != null)
                {
                    Left = null;
                    return true;
                }
                else
                {
                    RectangleF left = new RectangleF(oBounds.Left - sz, (oBounds.Top + 0.5f * oBounds.Height) - sz, 2 * sz, 2 * sz);                    
                    List<IDrawable> hits = Program.Model.FindItems(left);
                    if (hits.Count > 0)
                        foreach (IDrawable id in hits)
                        {
                            if (id != this && id.Parent == Parent)
                            {
                                if (id is PartGlyph)
                                {
                                    Left = (PartGlyph)id;
                                    UpdateLocation();
                                    return true;
                                }
                            }
                        }
                }
            }
            else
                if (oCorner == Corner.ControlPoint + 1)
                {
                    if (Right != null)
                    {
                        Right = null;
                        return true;
                    }
                    else
                    {
                        RectangleF right = new RectangleF(oBounds.Right - sz, (oBounds.Top + 0.5f * oBounds.Height) - sz, 2 * sz, 2 * sz);
                        List<IDrawable> hits = Program.Model.FindItems(right);
                        if (hits.Count > 0)
                            foreach (IDrawable id in hits)
                            {
                                if (id != this && id.Parent == Parent)
                                {
                                    if (id is PartGlyph)
                                    {
                                        Right = (PartGlyph)id;
                                        UpdateLocation();
                                        return true;
                                    }
                                }
                            }
                    }
                }
            
            return false;
        }
        /// <summary>
        /// Checks the rate expression and automatically makes all the modifier connectors
        /// </summary>
        public virtual void GenerateModifiers()
        {
            Part part = _Item as Part;
            if (part == null) return;

            foreach (Edge e in part.IncomingEdges)
            {
                if (e.Drawable != null)
                {
                    if (!_IncomingConnections.Contains(e.Drawable as Connector))
                    {
                        Program.Model.AddToModel(e.Drawable);
                        if (e.Drawable is Connector)
                            ((Connector)e.Drawable).ArrowHead = Connector.ArrowHeadType.Circle;
                    }
                }
                else
                {
                    e.GenerateDrawable(new PointF());
                    Program.Model.AddToModel(e.Drawable);
                    if (e.Drawable is Connector)
                        ((Connector)e.Drawable).ArrowHead = Connector.ArrowHeadType.Circle;
                }
                if (!string.IsNullOrEmpty(part.PoPS.Rate) && e.ReactantsHash.Count > 0 && e.Drawable is BezierConnector)
                    if (Edge.RateChangeWithRespectTo(part.PoPS.Rate, e.Reactants[0].ID) < 0)
                        ((BezierConnector)e.Drawable).ArrowHead = Connector.ArrowHeadType.Bar;
                    else
                        ((BezierConnector)e.Drawable).ArrowHead = Connector.ArrowHeadType.Circle;
            }

            Utility.Refresh();
        }
        public override void AddIncomingConnection(Connector connector)
        {
            base.AddIncomingConnection(connector);

            List<PartGlyph> connectedParts = ConnectedParts;

            bool allAnchored = true;

            foreach (IDrawable id in connector.Reactants)
            {
                bool anchored = false;
                List<IDrawable> visited = new List<IDrawable>();
                IDrawable source = id.AnchoredSource;
                while (source != null && !visited.Contains(source))
                {
                    visited.Add(source);
                    if (source is PartGlyph && connectedParts.Contains((PartGlyph)source))
                    {
                        anchored = true;
                        break;
                    }
                    source = source.AnchoredSource;
                }
                if (!anchored)
                {
                    allAnchored = false;
                    break;
                }
            }

            if (allAnchored)
                connector.AnchoredSource = this;
            //GenerateModifiers();
        }

        public override void WriteXML(XmlWriter writer)
        {
            base.WriteXML(writer);

            writer.WriteStartElement("PartGlyph", "");

            writer.WriteStartElement("PartColor");
            writer.WriteValue(_oPartColor.Name);
            writer.WriteEndElement();

            writer.WriteEndElement();

        }

        public override bool ReadXML(XmlReader reader)
        {
            bool read = base.ReadXML(reader);
            read = reader.Read();

            if (!read) return false;
            
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "PartGlyph")
            {
                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "PartColor":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oPartColor = Color.FromName(reader.ReadContentAsString());
                                
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }

                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "PartGlyph")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }
                return true;
            }
            return false;
        }

        public override void ShowToolTip(ToolTip tooltip, Control pnlCanvas, PointF location)
        {
            tooltip.ToolTipTitle = Type + ": ";
            tooltip.SetToolTip(pnlCanvas,
                String.Format(
                            "{3}ID:\t{0}"
                          + "{3}PoPS reverse:\t{1}"
                          + "{3}PoPS forward:\t{2}",
                Name,
                PoPS_Left,
                PoPS_Right,
                "\r\n"));

        }

        public override void ShowCornerToolTip(ToolTip tooltip, Control pnlcanvas, PointF location, Corner corner)
        {
            if (corner == Corner.ControlPoint || corner == Corner.ControlPoint + 1)
            {
                tooltip.SetToolTip(pnlcanvas,
                    "click and drag the red circle");
            }
            else
                base.ShowCornerToolTip(tooltip, pnlcanvas, location, corner);
        }


        public override void LoadDefaults(IDrawable drawable)
        {
            PartGlyph Default = drawable as PartGlyph;
            if (Default != null)
            {
                base.LoadDefaults(drawable);
                PartColor = Default.PartColor;
            }
        }

        public static PartGlyph DEFAULT = null;
    }
}
